%include {
	/* TODO: include these if debugging only */
	#include <stdio.h>
	#include <stdlib.h>
	#include "debug.h"
	#include "tokens.h"
	#include "language_scanner.h"
	#include "ast.h"
	#include "language_parser.h"
	#include "hash/ght_hash_table.h"
}

%token_type {token_value*}
%default_type {token_value*}
%extra_argument {AST_node *ast_root}

%type math_expr {AST_node*}
%type assignment {AST_node*}
%type DOUBLE_CONST {token_value*}
%type INTEGER_CONST {token_value*}
/* we would normally call it VARIABLE, but later we'll add functions */
%type LABEL {token_value*}


%right PRINT.
%left ADD SUB.
%left MUL DIV.
%left LPAREN RPAREN.
%left EOS.
%right ASSIGN.

/* this should be the last one;
it's used for creating custom ast node types, which are defined in files like
tokens.h, or used in ast.h
*/
%nonassoc DUMMY.

%token_prefix OP_
%syntax_error {
	DEBUG_PARSER1("syntax error");
}
%parse_accept {
	DEBUG_PARSER1("parse_accept");
}
%parse_failure {
	DEBUG_PARSER1("parse_failure");
}
%token_destructor {
	DEBUG_PARSER1("DTOR\ttoken");
}
%destructor math_expr {
	DEBUG_PARSER1("DTOR\tmath_expr");
}
%destructor DOUBLE_CONST {
	DEBUG_PARSER1("DTOR\tDOUBLE_CONST");
}
%destructor INTEGER_CONST {
	DEBUG_PARSER1("DTOR\tINTEGER_CONST");
}
%destructor LABEL {
	DEBUG_PARSER1("DTOR\tLABEL");
}


%start_symbol program

program ::= top_stmt_list(B) . {
	DEBUG_PARSER2("program ::= top_stmt_list(B)");
}
top_stmt_list(A) ::= top_stmt_list(B) top_stmt(C). {//multiline statement
	DEBUG_PARSER2("top_stmt_list(A) ::= top_stmt_list(B) top_stmt(C)");
}
top_stmt_list(A) ::= . {//uniline statement
	DEBUG_PARSER2("top_stmt_list(A) ::= .");
}
/*{{{*/
top_stmt(A) ::= PRINT math_expr(B) EOS . {
	DEBUG_PARSER2("top_stmt(A) ::= print math_expr(B)");
	}
/* 
top_stmt(A) ::= math_expr(B) . {
	DEBUG_PARSER2("top_stmt(A) ::= math_expr(B)");
}
*/
top_stmt(A) ::= assignment(B) EOS . {
	DEBUG_PARSER2("top_stmt(A) ::= assignment(B)");
	}
/*}}}*/
math_expr(A) ::= DOUBLE_CONST(B) . {
	A = ast_malloc_node(double_const,none,unary);
	if(!(A)) {
		//out of memory
	}
	A->val.unary.val = B;
	DEBUG_PARSER2("math_expr(A) ::= DOUBLE_CONST(%f)",B->val.d);
}
math_expr(A) ::= INTEGER_CONST(B) . {
	A = ast_malloc_node(integer_const,none,unary);
	if(!(A)) {
		//out of memory
	}
	A->val.unary.val = B;
	DEBUG_PARSER2("math_expr(%lld) ::= integer_number(%lld)",A->val.unary.val->val.i,B->val.i);
}
math_expr(A) ::= LABEL(B) . {
	A = ast_malloc_node(label,none,unary);
	if(!(A)) {
		//out of memory
	}
	A->val.unary.val = B;
	DEBUG_PARSER2("math_expr(A) ::= LABEL %s (rval)",B->val.str.val);
}

math_expr(A) ::= LPAREN math_expr(B) RPAREN . {
	A = ast_malloc_node(math_expr,none,chain);
	if(!(A)) {
		//out of memory
	}
	A->val.chain.val = B;
	DEBUG_PARSER3("math_expr ::= LPAREN math_expr(B) RPAREN");
}
math_expr(A) ::= math_expr(B) ADD math_expr(C) . {
	A = ast_malloc_node(add,left,binary);
	if(!(A)) {
		//out of memory
	}
	A->val.binary.left = B;
	A->val.binary.right = C;
	DEBUG_PARSER3("math_expr(A)::=math_expr(B) ADD math_expr(C)");
}
math_expr(A) ::= math_expr(B) SUB math_expr(C) . {
	A = ast_malloc_node(sub,left,binary);
	if(!(A)) {
		//out of memory
	}
	A->val.binary.left = B;
	A->val.binary.right = C;
	DEBUG_PARSER3("math_expr::=math_expr(B) SUB math_expr(C)");
}
math_expr(A) ::= math_expr(B) MUL math_expr(C) . {
	A = ast_malloc_node(mul,left,binary);
	if(!(A)) {
		//out of memory
	}
	A->val.binary.left = B;
	A->val.binary.right = C;
	DEBUG_PARSER3("math_expr::=math_expr(B) MUL math_expr(C)");
}
math_expr(A) ::= math_expr(B) DIV math_expr(C) . {
	A = ast_malloc_node(divide,left,binary);
	if(!(A)) {
		//out of memory
	}
	A->val.binary.left = B;
	A->val.binary.right = C;
	DEBUG_PARSER3("math_expr::=math_expr(B) DIV math_expr(C)");
}
/*
TODO: AST analysis: check if B is more than an unary label node;
    this should not be possible:
	foo+3=bar;

TODO: make this possible:
    	foo=bar=3;
*/
assignment(A) ::= math_expr(B) ASSIGN math_expr(C) . {
	A = ast_malloc_node(assignment,right,binary);
	if(!(A)) {
		//out of memory
	}
	A->val.binary.left = B;
	A->val.binary.right = C;
	DEBUG_PARSER3("assignment ::= LABEL(%s) ASSIGN math_expr(C) (lval)",B->val.unary.val->val.str.val);
}
